#include "sudoku.hpp"

using namespace std;

int Sudoku::stepCount = 0;

/* --------------------------------------------------------------- */
Sudoku::Sudoku() {
	reset();
}
/* --------------------------------------------------------------- */
Sudoku::Sudoku (Sudoku * input) {
	//cout << "Sudoku::Sudoku ENTER" << endl;
	reset();
	//cout << "Sudoku aaa" << endl;
	for(int i = 0; i < Sudoku::ROWS; i++)
		for(int j = 0; j < Sudoku::COLUMNS; j++)
			tab[i][j]->setValue(input->getField(i, j)->getValue());
	//cout << "Sudoku::Sudoku EXIT" << endl;
}
/* --------------------------------------------------------------- */

void Sudoku::createNeigSet(int r, int c) {
	//cout << "Sudoku::createNeigSet() ENTER" << endl;
	int i, j, rs, cs;
	
	Field * field = tab[r][c];
	
	//h-lines
    for(j = 0; j < 9; j++) {
		if(j != field->getColumn())
			field->neigs.insert(getField(field->getRow(), j));
    }
	
    //v-lines
    for(j = 0; j < 9; j++) {
		if(j != field->getRow())
			field->neigs.insert(getField(j, field->getColumn()));
    }
	
	//squares
	rs = (field->getRow() / 3) * 3;
	cs = (field->getColumn() / 3) * 3;
	for(i = rs; i < rs + 3; i++) {
		for(j = cs; j < cs + 3; j++) {
			if(i != field->getRow() && j != field->getColumn())
				field->neigs.insert(getField(i, j));
		}
	}
	
//	i = 1/ 0;
	
	//cout << "Sudoku::createNeigSet() EXIT" << endl;
}

/* --------------------------------------------------------------- */
void Sudoku::reset() {
	//cout << "Sudoku::reset() ENTER" << endl;
	int i, j;
	for(i = 0; i < Sudoku::ROWS; i++) {
		for(j = 0; j < Sudoku::COLUMNS; j++) {
//			cout << "reset bbb" << endl;
//			if(tab[i][j])
//				delete tab[i][j];
			cout << "this=" << this << endl;
			tab[i][j] = new Field(i, j, this);
		}
	}
//	cout << "reset(): aaa" << endl;
	//creating fields neigbourns
	for(i = 0; i < Sudoku::ROWS; i++) {
		for(j = 0; j < Sudoku::COLUMNS; j++) {
			tab[i][j]->createNeigSet();
			//createNeigSet(i, j);
		}
	}	
//	cout << "Sudoku::reset() EXIT" << endl;
}
/* --------------------------------------------------------------- */
Field * Sudoku::getField(int r, int c) {
	return tab[r][c];
}
/* --------------------------------------------------------------- */
//init of sudoku sheet
void Sudoku::fill() {
//	cout << "Sudoku::fill() ENTER" << endl;
	int mr, mc, i, j, v;
	for(mr = 0; mr < 9; mr += 3) {
		for(i = mr; i < mr + 3; i++) {
			for(j = mr; j < mr + 3; j++) {
				v = random.getInt(1, 9);
				while(getField(i, j)->checkValue(v) == false) {
					v = random.getInt(1, 9);
				}
				getField(i, j)->setValue(v);
			}
		}
	}	
//	cout << "Sudoku::fill() EXIT" << endl;
}
/* --------------------------------------------------------------- */
void Sudoku::print() {
	int i, j, val;
	cout << "--------------------------";
	for(i = 0; i < ROWS; i++) {
		if(i % 3 == 0)
			cout << endl;
		for(j = 0; j < COLUMNS; j++) {
			if(j % 3 == 0)
				cout << " ";
			val = tab[i][j]->getValue();
			if(val > 0)
				cout << val << " ";
			else
				cout << val << " ";
				//cout << "  ";				
		}
		cout << endl;
	}
}
/* --------------------------------------------------------------- */
int Sudoku::getTab() {
	int ret[9][9], i, j;
	for(i = 0; i < 9; i++)
		for(j =0; j < 9; j++)
			ret[i][j] = tab[i][j]->getValue();
	return 0;
}
/* --------------------------------------------------------------- */
std::vector<Sudoku *> * Sudoku::findSolution(int maxSol) {
	Sudoku::stepCount = 0;
	std::vector<Sudoku*> * ret = new std::vector<Sudoku*>();
	solutionNextStep(ret, maxSol, tab[0][0]);		//start finding of solution
//	cout << "Sudoku::findSolution EXIT" << endl;
	return ret;
}
/* --------------------------------------------------------------- */
void Sudoku::solutionNextStep(std::vector<Sudoku*>* sol, int maxSol, Field * f) {

//	cout << "solutionNextStep ENTER" << endl;
	//cout << "--------------------------------------------" << endl;

/*	solution->print();
	
	cout << "field: c=" << f->getColumn() << " r=" << f->getRow() << endl;
*/
	int cnt = 0, p = 0;
	int nr, nc;
	Sudoku * sud;
	
	Sudoku::stepCount++;	

//	cout << "aaa" << endl;

	if(f->getNoOfValues() == 0) {								//nie ma rozwiazania
		return;
	}
	
//	cout << "bbb" << endl;
		
	if(f->getValue() != 0) {									//pole ustawione wczesniej, nie ruszac, przejsc do nastepnego
		if(getNextField(f) != NULL) {
			solutionNextStep(sol, maxSol, getNextField(f));
			if(sol->size() > maxSol)
				return;

		} else {
			return;
		}
	}

//	cout << "ccc" << endl;

	for(p = 1; p < 10; p++) {
		if(f->checkValue(p) == true) {
//			cout << "ddd" << endl;
			f->setValue(p);
//			cout << "eee" << endl;
			if(getFreeFields() > 0) {
//				cout << "fff" << endl;
				solutionNextStep(sol, maxSol, getNextField(f));
//				cout << "ggg" << endl;
				if(sol->size() > maxSol)
					return;
			} else {
//				cout << "hhh" << endl;
				if(sol) {
//					cout << "jjj" << endl;
					//sud = new Sudoku();
//					cout << "kkk" << endl;
					sol->push_back(new Sudoku(this));
				}
			}
//			cout << "iii" << endl;
			f->setValue(0);
		}
	}
	
}
/* --------------------------------------------------------------- */
int Sudoku::getFreeFields() {
	int i, j, ret = 0;
	for(i = 0; i < 9; i++)
		for(j =0; j < 9; j++)
			if(tab[i][j]->getValue() == 0)
				ret++;
	return ret;
}
/* --------------------------------------------------------------- */
bool  Sudoku::setField(int v, int row, int col) {
	cout << "setField DUPA0 row=" << row << " col=" << col << " sizeof=" << sizeof(*tab) << endl;
	if(tab[row][col])
		cout << "setField DUPA4" << endl;
	else
		cout << "setField DUPA5" << endl;
		/*
	if(tab[row][col]->checkValue(v)) {
		cout << "setField DUPA1" << endl;
		tab[row][col]->setValue(v);
		return true;
	} else {
		cout << "setField DUPA2" << endl;
		return false;
	}*/
	return true;
}
/* --------------------------------------------------------------- */
Field * Sudoku::getNextField(Field * f) {
	Field * ret;
	if(f->getColumn() == 8) {
		//cout << "B solutionNextStep("<<i+1<<", 0);\n";
		//nr = i+1;
		//nc = 0;
		if(f->getRow() == 8)
			return NULL;
		return getField(f->getRow() + 1, 0);
	} else {
		//cout << "A solutionNextStep("<<i<<", "<<j+1<<");\n";
		//nr = i;
		//nc = j+1;
		return getField(f->getRow(), f->getColumn() + 1);
	}
}
/* --------------------------------------------------------------- */
